﻿using DotNetCommon.Data;
using NUnit.Framework;
using Shouldly;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DotNetCommon.Extensions;
using System.Collections;
using System.Collections.ObjectModel;
using Newtonsoft.Json.Linq;
using System.Diagnostics;
using System.Numerics;
using System.ComponentModel;
using System.Drawing;

namespace DotNetCommon.Test.Extensions
{
    [TestFixture]
    public class ObjectTests_DeepClone
    {
        [Test]
        public void Test_null()
        {
            object obj = null;
            var objNew = obj.DeepClone();
            objNew.ShouldBeNull();
        }

        [Test]
        public void Test_CloneSimple()
        {
            bool var_bool = true;
            var new_bool = var_bool.DeepClone();
            new_bool.ShouldBe(true);

            ThreadState var_enum = ThreadState.Terminated;
            var new_enum = var_enum.DeepClone();
            new_enum.ShouldBe(ThreadState.Terminated);

            char var_char = 'X';
            var new_char = var_char.DeepClone();
            new_char.ShouldBe('X');

            byte var_byte = 1;
            var new_byte = var_byte.DeepClone();
            new_byte.ShouldBe((byte)1);

            sbyte var_sbyte = 1;
            var new_sbyte = var_sbyte.DeepClone();
            new_sbyte.ShouldBe((sbyte)1);

            short var_short = 1;
            var new_short = var_short.DeepClone();
            new_short.ShouldBe((short)1);

            ushort var_ushort = 1;
            var new_ushort = var_ushort.DeepClone();
            new_ushort.ShouldBe((ushort)1);

            int var_int = 1;
            var new_int = var_int.DeepClone();
            new_int.ShouldBe(1);

            uint var_uint = 1;
            var new_uint = var_uint.DeepClone();
            new_uint.ShouldBe((uint)1);

            long var_long = 1;
            var new_long = var_long.DeepClone();
            new_long.ShouldBe(1);

            ulong var_ulong = 1;
            var new_ulong = var_ulong.DeepClone();
            new_ulong.ShouldBe((ulong)1);

            float var_float = 1;
            var new_float = var_float.DeepClone();
            new_float.ShouldBe((float)1);

            double var_double = 1;
            var new_double = var_double.DeepClone();
            new_double.ShouldBe((double)1);

            decimal var_decimal = 1;
            var new_decimal = var_decimal.DeepClone();
            new_decimal.ShouldBe((decimal)1);

            Guid var_guid = Guid.Parse("9ec99244-a71b-4fab-b933-639bfa90dddf");
            var new_guid = var_guid.DeepClone();
            new_guid.ShouldBe(var_guid);

            DateTime var_dt = DateTime.Parse("2022-01-01");
            var new_dt = var_dt.DeepClone();
            new_dt.ShouldBe(var_dt);

            DateTimeOffset var_dtoff = DateTimeOffset.Parse("2022-01-01 01:01:01 +08:00");
            var new_dtoff = var_dtoff.DeepClone();
            new_dtoff.ShouldBe(var_dtoff);

            DBNull var_dbnull = DBNull.Value;
            var new_dbnull = var_dbnull.DeepClone();
            new_dbnull.ShouldBe(var_dbnull);

            string var_string = "123";
            var new_string = var_string.DeepClone();
            new_string.ShouldBe(var_string);

#if NET6_0_OR_GREATER
            DateOnly var_dtonly = DateOnly.Parse("2022-01-01");
            var new_dtonly = var_dtonly.DeepClone();
            new_dtonly.ShouldBe(var_dtonly);

            TimeOnly var_tmonly = TimeOnly.Parse("01:01:01");
            var new_tmonly = var_tmonly.DeepClone();
            new_tmonly.ShouldBe(var_tmonly);
#endif


            //Vector
            Vector2 v2 = new Vector2(1, 2);
            var newV2 = v2.DeepClone();
            newV2.X = 5;
            Assert.IsTrue(v2.X == 1);
            Assert.IsTrue(newV2.X == 5);

            Vector3 v3 = new Vector3(1, 2, 3);
            var newV3 = v3.DeepClone();
            newV3.X = 5;
            Assert.IsTrue(v3.X == 1);
            Assert.IsTrue(newV3.X == 5);

            Vector4 v4 = new Vector4(1, 2, 3, 4);
            var newV4 = v4.DeepClone();
            newV4.X = 5;
            Assert.IsTrue(v4.X == 1);
            Assert.IsTrue(newV4.X == 5);


            Matrix3x2 matrix3X2 = new Matrix3x2(1, 2, 3, 4, 5, 6);
            var newMatrix3X2 = matrix3X2.DeepClone();
            newMatrix3X2.M11 = 10;
            Assert.IsTrue(matrix3X2.M11 == 1);
            Assert.IsTrue(newMatrix3X2.M11 == 10);

            Matrix4x4 matrix4x4 = new Matrix4x4(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
            var newMatrix4x4 = matrix4x4.DeepClone();
            newMatrix4x4.M11 = 10;
            Assert.IsTrue(matrix4x4.M11 == 1);
            Assert.IsTrue(newMatrix4x4.M11 == 10);

            Plane plane = new Plane(1, 2, 3, 4);
            var newPlane = plane.DeepClone();
            newPlane.Normal.X = 9;
            Assert.IsTrue(plane.Normal.X == 1);
            Assert.IsTrue(newPlane.Normal.X == 9);

            Quaternion quaternion = new Quaternion(1, 2, 3, 4);
            var newQuaternion = quaternion.DeepClone();
            newQuaternion.X = 9;
            Assert.IsTrue(quaternion.X == 1);
            Assert.IsTrue(newQuaternion.X == 9);

            //other
            Color color = Color.FromArgb(1, 1, 1, 1);
            var newColor = color.DeepClone();
            Assert.IsTrue(color == newColor);

            Point point = new Point(1, 2);
            var newPoint = point.DeepClone();
            newPoint.X = 9;
            Assert.IsTrue(point.X == 1);
            Assert.IsTrue(newPoint.X == 9);

            RectangleF rectangle = new RectangleF(1, 1, 1, 1);
            var newRectangle = rectangle.DeepClone();
            newRectangle.X = 9;
            Assert.IsTrue(rectangle.X == 1);
            Assert.IsTrue(newRectangle.X == 9);
        }

        #region Poco
        [Test]
        public void Test_ClonePojo()
        {
            var poco = new Poco
            {
                Id = 1,
                Name = "小明",
                Birth = DateTime.Parse("2022-01-02"),
                Field = "Field",
                FuProp = 3,
                FuField = "FuField",
                V2 = new Vector2(1, 2),
                V3 = new Vector3(1, 2, 3),
                intNullArray2_2 = new int?[][] { new int?[] { 1, null }, new int?[] { 2, 3 } }
            };
            var newPoco = poco.DeepClone();
            poco.ShouldNotBe(newPoco);
            Assert.IsTrue(newPoco.intNullArray2_2[0][0] == 1);
            Assert.IsTrue(newPoco.intNullArray2_2[0][1] == null);
            newPoco.Id = 2;
            newPoco.ToString().ShouldBe("id=2,name=小明,birth=2022-01-02");
            poco.ToString().ShouldBe("id=1,name=小明,birth=2022-01-02");

            newPoco.intNullArray2_2[1][1] = null;
            Assert.IsTrue(poco.intNullArray2_2[1][1] == 3);
        }

        class PojoFu
        {
            public int FuProp { get; set; }
            public string FuField;
        }

        class Poco : PojoFu
        {
            public int? intNull { get; set; }
            public int?[] intNullArray { get; set; }
            public int?[][] intNullArray2 { get; set; }
            public int?[][] intNullArray2_2 { get; set; }
            public Vector2? Vector2Null { get; set; }
            public Vector2?[] Vector2NullArray { get; set; }
            public List<Vector2?> Vector2NullList { get; set; }
            public Vector2 V2 { get; set; }
            public Vector3 V3 { get; set; }
            public string Field;
            public int Id { get; set; }
            public string Name { get; set; }
            public DateTime Birth { get; set; }
            public override string ToString()
            {
                return $"id={Id},name={Name},birth={Birth.ToString("yyyy-MM-dd")}";
            }
        }
        #endregion

        [Test]
        public void Test_CloneArray()
        {
            var arr = new int[] { 1, 2 };
            var newArr = arr.DeepClone();
            Assert.IsTrue(newArr != arr);
            newArr[0] = 2;
            arr.ToStringSeparated(",").ShouldBe("1,2");
            newArr.ToStringSeparated(",").ShouldBe("2,2");
        }

        [Test]
        public void Test_CloneIEnumerable()
        {
            IEnumerable<int> ienumerable = demo();
            var newienumerable = ienumerable.DeepClone();
            Assert.IsTrue(newienumerable != ienumerable);
            newienumerable.ToStringSeparated(",").ShouldBe("1,2");

            IEnumerable<int> demo()
            {
                yield return 1;
                yield return 2;
            }
        }

        [Test]
        public void Test_CloneList()
        {
            List<int> list = new List<int> { 1, 2, 3 };
            var newList = list.DeepClone();
            Assert.IsTrue(newList != list);
            newList.Add(1);
            newList.ToStringSeparated(",").ShouldBe("1,2,3,1");
            list.ToStringSeparated(",").ShouldBe("1,2,3");
        }

        [Test]
        public void Test_HashSet()
        {
            HashSet<int> set = new HashSet<int> { 1, 2, 3 };
            var newSet = set.DeepClone();
            Assert.IsTrue(newSet != set);
            newSet.Add(4);
            newSet.ToStringSeparated(",").ShouldBe("1,2,3,4");
            set.ToStringSeparated(",").ShouldBe("1,2,3");
        }

        [Test]
        public void Test_Dictionary()
        {
            Dictionary<int, string> dic = new Dictionary<int, string>
            {
                {1,"1" },
                {2,"2" },
                {3,"3" }
            };
            var newDic = dic.DeepClone();
            Assert.IsTrue(newDic != dic);
            newDic.Add(4, "4");
            newDic.Keys.ToStringSeparated(",").ShouldBe("1,2,3,4");
            dic.Keys.ToStringSeparated(",").ShouldBe("1,2,3");
        }

        [Test]
        public void Test_ValueTuple()
        {
            (int, string) tuple = (1, "小明");
            var newTuple = tuple.DeepClone();
            Assert.IsTrue(newTuple == tuple);
            newTuple.Item2 = "小花";
            tuple.Item2.ShouldBe("小明");
        }

        [Test]
        public void Test_LinkedList()
        {
            LinkedList<int> list = new LinkedList<int>();
            list.AddLast(1);
            list.AddLast(2);
            list.AddLast(3);

            var newList = list.DeepClone();
            Assert.IsTrue(newList != list);
            newList.AddLast(4);
            newList.ToStringSeparated(",").ShouldBe("1,2,3,4");
            list.ToStringSeparated(",").ShouldBe("1,2,3");
        }

        [Test]
        public void Test_Tuple()
        {
            Tuple<int, string> tuple = new Tuple<int, string>(1, "小明");

            var newTuple = tuple.DeepClone();
            Assert.IsTrue(newTuple == tuple);
            newTuple.Item1.ShouldBe(1);
            newTuple.Item2.ShouldBe("小明");
        }

        [Test]
        public void Test_ReadOnlyCollection()
        {
            var list = new List<int> { 1, 2 };
            ReadOnlyCollection<int> readList = new ReadOnlyCollection<int>(list);
            var newReadList = readList.DeepClone();

            Assert.IsTrue(newReadList != readList);
            list.Add(3);
            readList.ToStringSeparated(",").ShouldBe("1,2,3");
            newReadList.ToStringSeparated(",").ShouldBe("1,2");
        }

        #region CloneStruct
        [Test]
        public void Test_Struct()
        {
            var stru = new MyStruct { Id = 1, Name = "小明" };
            var newStru = stru.DeepClone();

            newStru.ToString().ShouldBe("id=1,name=小明");
        }

        struct MyStruct
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public override string ToString()
            {
                return $"id={Id},name={Name}";
            }
        }
        #endregion

        [Test]
        public void Test_Anonymous()
        {
            var obj = new
            {
                Id = 1,
                Name = "小明"
            };
            var newObj = obj.DeepClone();
            Assert.IsTrue(obj == newObj);
        }

        [Test]
        public void Test_JObject_JArray()
        {
            //无需验证 JToken 本身有 DeepClone 方法
            var json = new { Id = 1, Name = "小明" }.ToJson();
            var json2 = new[] { new { Id = 1, Name = "小明" } }.ToJson();
        }

        #region RegisterCloneHander
        [Test]
        public void Test_RegisterCloneHander()
        {
            DeepCloneHelper.RegisterCloneHander(typeof(MyClass), (obj, cache) =>
            {
                if (cache.ContainsKey(obj)) return cache.get_Item(obj);
                var res = new MyClass((obj as MyClass).Id);
                cache.set_Item(obj, res);
                return res;
            });
            var cls = new MyClass(1);
            var newCls = cls.DeepClone();
            Assert.IsTrue(cls != newCls);
            newCls.Id.ShouldBe(1);
        }
        class MyClass
        {
            public MyClass(int id)
            {
                Id = id;
            }

            public int Id { get; set; }
        }
        #endregion
    }

    [TestFixture]
    public class ObjectTests_DeepClone2
    {
        #region Poco
        [Test]
        public void Test_ClonePojo()
        {
            var demoRefer = new DemoRefer { Id = 1 };

            var poco = new Poco
            {
                Id = 1,
                Name = "小明",
                ValueTuple = (1, "夏明"),
                ValueTuple2 = (1, "夏明", demoRefer),
                Array = new[] { demoRefer },
                Dictionary = new Dictionary<DemoRefer, DemoRefer> { { demoRefer, demoRefer } },
                HashSet = new HashSet<DemoRefer> { demoRefer },
                JArray = new[] { new { Id = 1, Name = "小明" } }.ToJson().ToObject<JArray>(),
                JObject = new { Id = 1, Name = "小明" }.ToJson().ToObject<JObject>(),
                JToken = new { Id = 1, Name = "小明" }.ToJson().ToObject<JObject>(),
                List = new List<DemoRefer> { demoRefer },
                Tuple = new Tuple<DemoRefer, DemoRefer>(demoRefer, demoRefer),
                LinkedList = new LinkedList<DemoRefer>(new[] { demoRefer, demoRefer }),
                ReadOnlyCollection = new ReadOnlyCollection<DemoRefer>(new[] { demoRefer })
            };
            demoRefer.Poco = poco;

            var newPoco = poco.DeepClone();

            Assert.IsTrue(newPoco != poco);
            newPoco.Id.ShouldBe(1);
            newPoco.Name.ShouldBe("小明");
            //ValueTuple & ValueTuple2
            newPoco.ValueTuple.ShouldBe((1, "夏明"));
            newPoco.ValueTuple2.Item1.ShouldBe(1);
            newPoco.ValueTuple2.Item2.ShouldBe("夏明");
            Assert.IsTrue(newPoco.ValueTuple2.Item3 != poco.ValueTuple2.Item3);
            Assert.IsTrue(newPoco.ValueTuple2.Item3.Id == 1);
            Assert.IsTrue(newPoco.ValueTuple2.Item3.Poco == newPoco);

            //Array
            Assert.IsTrue(newPoco.Array != poco.Array);
            Assert.IsTrue(newPoco.Array[0] == newPoco.ValueTuple2.Item3);

            //Dictionary
            Assert.IsTrue(newPoco.Dictionary != poco.Dictionary);
            Assert.IsTrue(newPoco.Dictionary[newPoco.ValueTuple2.Item3] == newPoco.ValueTuple2.Item3);

            //HashSet
            Assert.IsTrue(newPoco.HashSet != poco.HashSet);
            Assert.IsTrue(newPoco.HashSet.FirstOrDefault() == newPoco.ValueTuple2.Item3);

            //JArray & JObject & JToken
            Assert.IsTrue(newPoco.JArray != poco.JArray);
            Assert.IsTrue(newPoco.JArray.ToJson() == poco.JArray.ToJson());
            Assert.IsTrue(newPoco.JObject != poco.JObject);
            Assert.IsTrue(newPoco.JObject.ToJson() == poco.JObject.ToJson());
            Assert.IsTrue(newPoco.JToken != poco.JToken);
            Assert.IsTrue(newPoco.JToken.ToJson() == poco.JToken.ToJson());

            //List
            Assert.IsTrue(newPoco.List != poco.List);
            Assert.IsTrue(newPoco.List.FirstOrDefault() == newPoco.ValueTuple2.Item3);

            //Tuple
            Assert.IsTrue(newPoco.Tuple != poco.Tuple);
            Assert.IsTrue(newPoco.Tuple.Item1 == newPoco.Tuple.Item2);
            Assert.IsTrue(newPoco.Tuple.Item1 == newPoco.ValueTuple2.Item3);

            //LinkedList
            Assert.IsTrue(newPoco.LinkedList != poco.LinkedList);
            Assert.IsTrue(newPoco.LinkedList.FirstOrDefault() == newPoco.LinkedList.LastOrDefault());
            Assert.IsTrue(newPoco.LinkedList.FirstOrDefault() == newPoco.ValueTuple2.Item3);

            //ReadOnlyCollection
            Assert.IsTrue(newPoco.ReadOnlyCollection != poco.ReadOnlyCollection);
            Assert.IsTrue(newPoco.ReadOnlyCollection.FirstOrDefault() == newPoco.ValueTuple2.Item3);

        }

        class DemoRefer { public int Id { get; set; } public Poco Poco { get; set; } }

        class Poco
        {
            public int Id { get; set; }
            public string Name { get; set; }

            public (int, string) ValueTuple { get; set; }
            public (int, string, DemoRefer) ValueTuple2 { get; set; }

            public Tuple<DemoRefer, DemoRefer> Tuple { get; set; }

            public List<DemoRefer> List { get; set; }
            public DemoRefer[] Array { get; set; }
            public Dictionary<DemoRefer, DemoRefer> Dictionary { get; set; }

            public ReadOnlyCollection<DemoRefer> ReadOnlyCollection { get; set; }
            public HashSet<DemoRefer> HashSet { get; set; }
            public LinkedList<DemoRefer> LinkedList { get; set; }
            public JObject JObject { get; set; }
            public JArray JArray { get; set; }
            public JToken JToken { get; set; }
        }
        #endregion

        class NormalPojo
        {
            public int Id { get; set; }
            public string Name { get; set; }
        }
        [Test]
        public void Test_CloneArray()
        {
            var arr = new NormalPojo[] { new NormalPojo { Id = 1, Name = "小明" } };
            var newArr = arr.DeepClone();
            Assert.IsTrue(newArr != arr);
            newArr[0].Id = 2;
            arr[0].Id.ShouldBe(1);

            arr = new[] { arr[0], arr[0] };
            newArr = arr.DeepClone();
            Assert.IsTrue(newArr != arr);
            Assert.IsTrue(newArr[0] == newArr[1]);
        }

        [Test]
        public void Test_CloneIEnumerable()
        {
            IEnumerable<NormalPojo> ienumerable = demo();
            var newienumerable = ienumerable.DeepClone();
            Assert.IsTrue(newienumerable != ienumerable);

            Assert.IsTrue(newienumerable.FirstOrDefault() != ienumerable.FirstOrDefault());
            Assert.IsTrue(newienumerable.LastOrDefault() != ienumerable.LastOrDefault());

            newienumerable.FirstOrDefault().Id.ShouldBe(1);
            newienumerable.FirstOrDefault().Name.ShouldBe("小明");

            newienumerable.LastOrDefault().Id.ShouldBe(2);
            newienumerable.LastOrDefault().Name.ShouldBe("小花");

            IEnumerable<NormalPojo> demo()
            {
                yield return new NormalPojo { Id = 1, Name = "小明" };
                yield return new NormalPojo { Id = 2, Name = "小花" };
            }
        }

        [Test]
        public void Test_CloneList()
        {
            List<NormalPojo> list = new List<NormalPojo> { new NormalPojo { Id = 1, Name = "小明" }, new NormalPojo { Id = 2, Name = "小花" } };
            var newList = list.DeepClone();
            Assert.IsTrue(newList != list);

            Assert.IsTrue(newList.FirstOrDefault() != list.FirstOrDefault());
            Assert.IsTrue(newList.LastOrDefault() != list.LastOrDefault());

            newList.FirstOrDefault().Id.ShouldBe(1);
            newList.FirstOrDefault().Name.ShouldBe("小明");

            newList.LastOrDefault().Id.ShouldBe(2);
            newList.LastOrDefault().Name.ShouldBe("小花");

        }

        [Test]
        public void Test_HashSet()
        {
            HashSet<NormalPojo> list = new HashSet<NormalPojo> { new NormalPojo { Id = 1, Name = "小明" }, new NormalPojo { Id = 2, Name = "小花" } };
            var newList = list.DeepClone();
            Assert.IsTrue(newList != list);

            Assert.IsTrue(newList.FirstOrDefault() != list.FirstOrDefault());
            Assert.IsTrue(newList.LastOrDefault() != list.LastOrDefault());

            newList.FirstOrDefault().Id.ShouldBe(1);
            newList.FirstOrDefault().Name.ShouldBe("小明");

            newList.LastOrDefault().Id.ShouldBe(2);
            newList.LastOrDefault().Name.ShouldBe("小花");
        }

        [Test]
        public void Test_Dictionary()
        {
            Dictionary<int, NormalPojo> list = new Dictionary<int, NormalPojo> { { 1, new NormalPojo { Id = 1, Name = "小明" } }, { 2, new NormalPojo { Id = 2, Name = "小花" } } };
            var newList = list.DeepClone();
            Assert.IsTrue(newList != list);

            Assert.IsTrue(newList[1] != list[1]);
            Assert.IsTrue(newList[2] != list[2]);

            newList[1].Id.ShouldBe(1);
            newList[1].Name.ShouldBe("小明");

            newList[2].Id.ShouldBe(2);
            newList[2].Name.ShouldBe("小花");
        }

        [Test]
        public void Test_ValueTuple()
        {
            (NormalPojo, NormalPojo) tuple = (new NormalPojo { Id = 1, Name = "小明" }, new NormalPojo { Id = 2, Name = "小花" });
            var newTuple = tuple.DeepClone();
            Assert.IsTrue(newTuple != tuple);
            Assert.IsTrue(newTuple.Item1 != tuple.Item1);
            Assert.IsTrue(newTuple.Item2 != tuple.Item2);

            newTuple.Item1.Id.ShouldBe(1);
            newTuple.Item1.Name.ShouldBe("小明");

            newTuple.Item2.Id.ShouldBe(2);
            newTuple.Item2.Name.ShouldBe("小花");
        }

        [Test]
        public void Test_LinkedList()
        {
            LinkedList<NormalPojo> list = new LinkedList<NormalPojo>();
            list.AddLast(new NormalPojo { Id = 1, Name = "小明" });
            list.AddLast(new NormalPojo { Id = 2, Name = "小花" });

            var newList = list.DeepClone();
            Assert.IsTrue(newList != list);
            Assert.IsTrue(newList.FirstOrDefault() != list.FirstOrDefault());
            Assert.IsTrue(newList.LastOrDefault() != list.LastOrDefault());

            newList.FirstOrDefault().Id.ShouldBe(1);
            newList.FirstOrDefault().Name.ShouldBe("小明");
            newList.LastOrDefault().Id.ShouldBe(2);
            newList.LastOrDefault().Name.ShouldBe("小花");
        }

        [Test]
        public void Test_Tuple()
        {
            Tuple<NormalPojo, NormalPojo> tuple = new Tuple<NormalPojo, NormalPojo>(new NormalPojo { Id = 1, Name = "小明" }, new NormalPojo { Id = 2, Name = "小花" });

            var newTuple = tuple.DeepClone();
            Assert.IsTrue(newTuple != tuple);
            Assert.IsTrue(newTuple.Item1 != tuple.Item1);
            Assert.IsTrue(newTuple.Item2 != tuple.Item2);

            newTuple.Item1.Id.ShouldBe(1);
            newTuple.Item1.Name.ShouldBe("小明");

            newTuple.Item2.Id.ShouldBe(2);
            newTuple.Item2.Name.ShouldBe("小花");
        }

        [Test]
        public void Test_ReadOnlyCollection()
        {
            var list = new List<NormalPojo> { new NormalPojo { Id = 1, Name = "小明" }, new NormalPojo { Id = 2, Name = "小花" } };
            ReadOnlyCollection<NormalPojo> readList = new ReadOnlyCollection<NormalPojo>(list);
            var newReadList = readList.DeepClone();

            Assert.IsTrue(newReadList != readList);
            list.Add(new NormalPojo { Id = 3, Name = "小刚" });

            newReadList.Count.ShouldBe(2);
            newReadList[0].Id.ShouldBe(1);
            newReadList[0].Name.ShouldBe("小明");
            newReadList[1].Id.ShouldBe(2);
            newReadList[1].Name.ShouldBe("小花");
        }

        #region CloneStruct
        [Test]
        public void Test_Struct()
        {
            var stru = new MyStruct { Id = 1, Name = "小明", NormalPojo = new NormalPojo { Id = 2, Name = "小花" } };
            var newStru = stru.DeepClone();

            Assert.IsTrue(newStru.NormalPojo != stru.NormalPojo);
            newStru.NormalPojo.Id.ShouldBe(2);
            newStru.NormalPojo.Name.ShouldBe("小花");

        }

        struct MyStruct
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public NormalPojo NormalPojo { get; set; }
        }
        #endregion

        [Test]
        public void Test_Anonymous()
        {
            var obj = new
            {
                Id = 1,
                Name = "小明",
                NormalPojo = new NormalPojo { Id = 2, Name = "小花" }
            };
            var newObj = obj.DeepClone();
            Assert.IsTrue(obj != newObj);
            Assert.IsTrue(obj.NormalPojo != newObj.NormalPojo);

            newObj.NormalPojo.Id.ShouldBe(2);
            newObj.NormalPojo.Name.ShouldBe("小花");
        }

        [Test]
        public void Test_JObject_JArray()
        {
            var json = new { Id = 1, Name = "小明" }.ToJson();
            var json2 = new[] { new { Id = 1, Name = "小明" } }.ToJson();

            var list = new List<JToken>() { json.ToObject<JObject>(), json2.ToObject<JArray>() };
            var newList = list.DeepClone();

            Assert.IsTrue(list != newList);
            Assert.IsTrue(newList[0] != list[0]);
            Assert.IsTrue(newList[1] != list[1]);

            newList[0]["Id"].Value<int>().ShouldBe(1);
            newList[0]["Name"].Value<string>().ShouldBe("小明");

            newList[1][0]["Id"].Value<int>().ShouldBe(1);
            newList[1][0]["Name"].Value<string>().ShouldBe("小明");

        }

        [Test]
        public void DeppCloneNoCtorErrorTest()
        {
            var obj = new NoCtor(12);
            try
            {
                var newObj = obj.DeepClone();
            }
            catch (Exception ex)
            {
                Assert.IsTrue(ex.Message.Contains("必须有空参构造函数"));
            }
        }

        public class NoCtor
        {
            public NoCtor(int id)
            {
                Id = id;
            }

            public int Id { get; set; }
        }

        /// <summary>
        /// 测试循环引用
        /// </summary>
        [Test]
        public void CircleTest()
        {
            List<PersonCircle> list = new List<PersonCircle>()
            {
                new PersonCircle()
                {
                    Id=1,
                    Name="小明",
                    Pwd="xiaopming",
                    Book=new BookCircle()
                    {
                        Id=1,
                        Name="语文"
                    }
                },
                new PersonCircle()
                {
                    Id=1,
                    Name="小王",
                    Pwd="wang",
                    Book=new BookCircle()
                    {
                        Id=2,
                        Name="数学"
                    }
                }
            };
            list[0].Book.Person = list[0];
            list[1].Book.Person = list[1];
            var newList = list.DeepClone();

            Assert.IsTrue(list != newList);
            Assert.IsTrue(list[0] != newList[0]);
            Assert.IsTrue(list[0].Book != newList[0].Book);
            Assert.IsTrue(list[1] != newList[1]);
            Assert.IsTrue(list[1].Book != newList[1].Book);

            Assert.IsTrue(newList[0].Book.Person == newList[0]);
            Assert.IsTrue(newList[1].Book.Person == newList[1]);
        }

        #region 循环引用
        public class PersonCircle
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public string Pwd { get; set; }
            public BookCircle Book { set; get; }
        }

        public class BookCircle
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public PersonCircle Person { get; set; }
        }
        #endregion
    }
}
